package org.opensuse.bs.ui.views;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ITreeSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.forms.events.ExpansionAdapter;
import org.eclipse.ui.forms.events.ExpansionEvent;
import org.eclipse.ui.forms.events.HyperlinkAdapter;
import org.eclipse.ui.forms.events.HyperlinkEvent;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Hyperlink;
import org.eclipse.ui.forms.widgets.ScrolledForm;
import org.eclipse.ui.forms.widgets.Section;
import org.eclipse.ui.forms.widgets.TableWrapData;
import org.eclipse.ui.forms.widgets.TableWrapLayout;
import org.eclipse.ui.navigator.CommonNavigator;
import org.eclipse.ui.part.ViewPart;
import org.opensuse.bs.ui.BSUIPlugin;
import org.opensuse.bs.ui.IBSUIConstants;
import org.opensuse.bs.ui.OSC;
import org.opensuse.bs.ui.model.BSModelElement;
import org.opensuse.bs.ui.model.RemotePackage;
import org.opensuse.bs.ui.model.RemoteProject;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

public class BuildResultView extends ViewPart implements ISelectionListener {
	private FormToolkit toolkit;
	private ScrolledForm form;
	
	private Action refreshAction;
	private Action rebuildAction;
	
	private ITreeSelection selected;
	
	private class ShowLogDialog extends Dialog {
		private File content;
		
		public ShowLogDialog(Shell parentShell, File file) {
			super(parentShell);
			content = file;
		}
		
		protected Control createDialogArea(Composite parent) {
			Composite client = new Composite(parent, SWT.NONE);
			GridLayout layout = new GridLayout();
			client.setLayout(layout);
			Text text = new Text(client, SWT.BORDER|SWT.MULTI|SWT.WRAP|SWT.V_SCROLL|SWT.H_SCROLL);
			GridData gd = new GridData();
			text.setLayoutData(gd);
			text.setEditable(false);
			gd.horizontalAlignment = SWT.FILL;
			gd.grabExcessHorizontalSpace = true;
			gd.widthHint = 600;
			gd.heightHint = 500;
			if(content.exists()) {
				StringBuffer buf = new StringBuffer();
				try {
					FileReader in = new FileReader(content);
					BufferedReader input = new BufferedReader(in);
					String line;
					while((line = input.readLine()) != null) {
						buf.append(line+System.getProperty("line.separator"));
					}
					text.setText(buf.toString());
				}catch(Exception e) {
					e.printStackTrace();
				}
			}
			return client;
		}
		
		protected Control createButtonBar(Composite parent) {return null;}
	}
	
	private void updateRemoteBuildInof(ITreeSelection aim) {
		TreePath path = aim.getPaths()[0];
		BSModelElement bse = (BSModelElement) path.getLastSegment();
		Control[] children = form.getBody().getChildren();
		for(Control child : children) {
			child.dispose();
		}
		if(bse instanceof RemotePackage) {
			final String prj = ((BSModelElement) path.getFirstSegment()).getName();
			final String pkg = bse.getName();
			
			form.setText("Build Status for Package "+pkg);
			TableWrapLayout layout = new TableWrapLayout();
			form.getBody().setLayout(layout);
			
			Section section = toolkit.createSection(form.getBody(), 
					Section.TITLE_BAR|Section.DESCRIPTION|Section.EXPANDED|Section.TWISTIE);
			section.addExpansionListener(new ExpansionAdapter() {
				public void expansionStateChanged(ExpansionEvent e) {
					form.reflow(true);
				}
			});
			TableWrapData td = new TableWrapData(TableWrapData.FILL_GRAB);
			section.setLayoutData(td);
			section.setText("Build Status");
			section.setDescription(
					"Show build status and binaries for this package.");
			toolkit.createCompositeSeparator(section);
			final Composite client = toolkit.createComposite(section);
			GridLayout glayout = new GridLayout();
			client.setLayout(glayout);
			section.setClient(client);
			form.reflow(true);
			
			Job job = new Job("Getting the build status for "+pkg) {
				protected IStatus run(IProgressMonitor monitor) {
					try {
						Document _repo = OSC.toXML("meta prj "+prj);
						if(_repo == null) {
							return Status.CANCEL_STATUS;
						}
						final NodeList repolist = _repo.getElementsByTagName("repository");
						if(repolist.getLength() == 0) {
							return Status.CANCEL_STATUS;
						}
						final Map<String, Document[]> bmap = new HashMap<String, Document[]>(); //for binary
						final Map<String, Document[]> smap = new HashMap<String, Document[]>(); //for build status
						for(int i=0;i<repolist.getLength();i++) {
							Element repo = (Element) repolist.item(i);
							String reponame = repo.getAttribute("name");
							NodeList archs = repo.getElementsByTagName("arch");
							Document[] docs = new Document[2];
							Document[] status = new Document[2];
							String cmd1 = "req -m GET /build/"+prj+"/"+reponame+"/i586/"+pkg;
							String cmd2 = "req -m GET /build/"+prj+"/"+reponame+"/x86_64/"+pkg;
							if(archs.getLength() == 2) {
								docs[0] = OSC.toXML(cmd1);
								docs[1] = OSC.toXML(cmd2);
								status[0] = OSC.toXML(cmd1+"/_status");
								status[1] = OSC.toXML(cmd2+"/_status");
							}
							else {
								if(archs.item(0).getTextContent().equals("i586")) {
									docs[0] = OSC.toXML(cmd1);
									docs[1] = null;
									status[0] = OSC.toXML(cmd1+"/_status");
									status[1] = null;
								}
								else {
									docs[0] = null;
									docs[1] = OSC.toXML(cmd2);
									status[0] = null;
									status[1] = OSC.toXML(cmd2+"/_status");
								}
							}
							bmap.put(reponame, docs);
							smap.put(reponame, status);
						}
						
						Display display = form.getDisplay();
						display.asyncExec(new Runnable() {
							public void run() {
								try {
									for(int i=0;i<repolist.getLength();i++) {
										final String repo = ((Element) repolist.item(i)).getAttribute("name");
										Section rsection = toolkit.createSection(client, Section.EXPANDED|Section.TWISTIE);
										rsection.addExpansionListener(new ExpansionAdapter() {
											public void expansionStateChanged(ExpansionEvent e) {
												form.reflow(true);
											}
										});
										rsection.setText(repo);
										GridData gd = new GridData();
										rsection.setLayoutData(gd);
										gd.horizontalAlignment = SWT.FILL;
										gd.grabExcessHorizontalSpace = true;
										toolkit.createCompositeSeparator(rsection);
										
										GridLayout glayout = new GridLayout();
										Composite rclient = toolkit.createComposite(rsection);
										rclient.setLayout(glayout);
										glayout.numColumns = 2;
										rsection.setClient(rclient);
										
										String[] archname = new String[]{"i586", "x86_64"};
										for(int j=0;j<2;j++) {
											Document statusDoc = smap.get(repo)[j];
											if(statusDoc == null) {
												continue;
											}
											
											Section arch_section = toolkit.createSection(rclient, Section.TITLE_BAR|Section.DESCRIPTION);
											gd = new GridData();
											arch_section.setLayoutData(gd);
											gd.grabExcessHorizontalSpace = true;
											gd.horizontalAlignment = SWT.FILL;
											arch_section.setText(archname[j]);
											Composite arch_client = toolkit.createComposite(arch_section);
											glayout = new GridLayout();
											arch_client.setLayout(glayout);
											arch_section.setClient(arch_client);
											String buildStatus = statusDoc.getDocumentElement().getAttribute("code");
											arch_section.setDescription("Build Status: "+buildStatus);
											
											final String arch_name = archname[j];
											Hyperlink link = toolkit.createHyperlink(arch_client, "Build Log", SWT.WRAP);
											link.addHyperlinkListener(new HyperlinkAdapter() {
												public void linkActivated(HyperlinkEvent e) {
													Job logJob = new Job("Getting build log for "+pkg) {
														public IStatus run(IProgressMonitor monitor) {
															try {
																Vector<String> log = 
																	OSC.run("req -m GET /build/"+prj+"/"+repo+"/"+arch_name+"/"+pkg+"/_log");
																if(log.isEmpty()) {
																	return Status.CANCEL_STATUS;
																}
																final File logFile = File.createTempFile("build"+pkg, "log");
																FileWriter out = new FileWriter(logFile);
																BufferedWriter output = new BufferedWriter(out);
																for(String line : log) {
																	output.write(line);
																	output.newLine();
																}
																output.close();
																Display display = form.getDisplay();
																display.asyncExec(new Runnable() {
																	public void run() {
																		Dialog dialog = new ShowLogDialog(form.getShell(), logFile);
																		dialog.open();
																	}
																});
															}catch(Exception e) {
																e.printStackTrace();
															}
															return Status.OK_STATUS;
														}
													};
													logJob.schedule();
												}
											});
											
											if(buildStatus.equals("excluded")
													|| buildStatus.equals("broken") 
													|| buildStatus.equals("blocked")) {
												link.setEnabled(false);
											}
											
											Document binaryDoc = bmap.get(repo)[j];
											if(binaryDoc == null) {
												continue;
											}
											NodeList binaryList = binaryDoc.getElementsByTagName("binary");
											if(binaryList.getLength() != 0) {
												TableViewer tv = new TableViewer(arch_client, SWT.BORDER|SWT.V_SCROLL);
												gd = new GridData();
												tv.getControl().setLayoutData(gd);
												gd.grabExcessHorizontalSpace = true;
												gd.horizontalAlignment = SWT.FILL;
												
												Table t = tv.getTable();
												t.setHeaderVisible(true);
												t.setLinesVisible(true);
												TableColumn column1 = new TableColumn(t, SWT.NONE);
												column1.setText("Binary built");
												column1.setWidth(120);
												TableColumn column2 = new TableColumn(t, SWT.NONE);
												column2.setText("Binary size (Byte)");
												column2.setWidth(120);
												TableColumn column3 = new TableColumn(t, SWT.NONE);
												column3.setText("Creation time");
												column3.setWidth(60);
												
												for(int k=0;k<binaryList.getLength();k++) {
													Element binary = (Element) binaryList.item(k);
													String[] tmp = new String[3];
													tmp[0] = binary.getAttribute("filename");
													tmp[1] = binary.getAttribute("size");
													long mtime = Long.parseLong(binary.getAttribute("mtime")+"000");
													Date date = new Date(mtime);
													tmp[2] = date.toString();
													TableItem item = new TableItem(t, SWT.NONE);
													item.setText(tmp);
												}
											}
										}
									}
									client.layout();
									form.reflow(true);
								}catch(Exception e) {
									e.printStackTrace();
								}
							}
						});
					}catch(Exception e) {
						e.printStackTrace();
					}
					return Status.OK_STATUS;
				}
			};
			job.schedule();
		}else if(bse instanceof RemoteProject) {
			final String prj = bse.getName();
			form.setText("Build Status for Project "+prj);
			
			TableWrapLayout layout = new TableWrapLayout();
			form.getBody().setLayout(layout);
			
			Section psection = toolkit.createSection(form.getBody(), 
					Section.TITLE_BAR|Section.DESCRIPTION|Section.EXPANDED|Section.TWISTIE);
			psection.addExpansionListener(new ExpansionAdapter() {
				public void expansionStateChanged(ExpansionEvent e) {
					form.reflow(true);
				}
			});
			TableWrapData td = new TableWrapData(TableWrapData.FILL_GRAB);
			psection.setLayoutData(td);
			psection.setText("Build Repositories");
			psection.setDescription("Statistics of build results for all packages");
			toolkit.createCompositeSeparator(psection);
			Composite pclient = toolkit.createComposite(psection);
			GridLayout glayout = new GridLayout();
			pclient.setLayout(glayout);
			psection.setClient(pclient);
			
			final Table table = toolkit.createTable(pclient, SWT.BORDER);
			GridData gd = new GridData();
			table.setLayoutData(gd);
			gd.grabExcessHorizontalSpace = true;
			gd.horizontalAlignment = SWT.FILL;
			gd.heightHint = 150;
			table.setHeaderVisible(true);
			table.setLinesVisible(true);
			TableColumn col1 = new TableColumn(table, SWT.NONE);
			col1.setText("Repo Name");
			col1.setWidth(150);
			TableColumn col2 = new TableColumn(table, SWT.NONE);
			col2.setText("i586");
			col2.setWidth(200);
			TableColumn col3 = new TableColumn(table, SWT.NONE);
			col3.setText("x86_64");
			col3.setWidth(200);
			
			form.reflow(true);
			Job job = new Job("Getting statistics") {
				protected IStatus run(IProgressMonitor monitor) {
					try {
						Document _repo = OSC.toXML("req -m GET /build/"+prj);
						if(_repo == null) {
							return Status.CANCEL_STATUS;
						}
						Document _result = OSC.toXML("req -m GET /build/"+prj+"/_result");
						if(_result == null) {
							return Status.CANCEL_STATUS;
						}
						NodeList repos = _repo.getElementsByTagName("entry");
						final int repo_size = repos.getLength();
						if(repos.getLength() != 0) {
							final String[] reponame = new String[repo_size];
							for(int i=0;i<repo_size;i++) {
								reponame[i] = 
									((Element) repos.item(i)).getAttribute("name");
							}
							final NodeList r = _result.getElementsByTagName("result");
							Display display = form.getDisplay();
							display.asyncExec(new Runnable() {
								public void run() {
									for(int i=0;i<repo_size;i++) {
										String[] tmp = new String[3];
										tmp[0] = reponame[i];
										for(int j=0;j<r.getLength();j++) {
											Element one = (Element) r.item(j);
											if(one.getAttribute("repository").equals(reponame[i])) {
												NodeList statuslist = one.getElementsByTagName("status");
												Map<String, Integer> map = new HashMap<String, Integer>();
												for(int k=0;k<statuslist.getLength();k++) {
													String status = ((Element) statuslist.item(k)).getAttribute("code");
													Integer pkg_num = map.get(status);
													if(pkg_num == null) {
														map.put(status, 0);
													}
													else {
														map.put(status, pkg_num+1);
													}
												}
												Set<String> keys = map.keySet();
												if(one.getAttribute("arch").equals("i586")) {
													tmp[1] = "";
													for(String key : keys) {
														tmp[1] = tmp[1]+key+":"+map.get(key).toString()+"  ";
													}
													tmp[1] = tmp[1].trim();
												}else if(one.getAttribute("arch").equals("x86_64")) {
													tmp[2] = "";
													for(String key : keys) {
														tmp[2] = tmp[2]+key+":"+map.get(key).toString()+"  ";
													}
													tmp[2] = tmp[2].trim();
												}
											}
										}
										TableItem item = new TableItem(table, SWT.NONE);
										item.setText(tmp);
									}
									form.getBody().layout();
								}
							});
						}
					}catch(Exception e) {
						e.printStackTrace();
					}
					
					return Status.OK_STATUS;
				}
			};
			job.schedule();
		}
	}
	
	public void createPartControl(Composite parent) {
		toolkit = new FormToolkit(parent.getDisplay());
		form = toolkit.createScrolledForm(parent);
		form.setText("Build Result");
		
		getViewSite().getPage().addSelectionListener(this);
		BSUIPlugin plugin = BSUIPlugin.getDefault();
		refreshAction = new Action("Refresh") {
			public void run() {
				if(selected != null) {
					BuildResultView.this.updateRemoteBuildInof(selected);
				}
			}
		};
		refreshAction.setHoverImageDescriptor(
				plugin.getImageDescriptor(IBSUIConstants.IMG_REFRESH));
		refreshAction.setToolTipText("Refresh BuildResult view " +
				"according to the current selected item in remote view.");
		IActionBars bars = getViewSite().getActionBars();
		IToolBarManager tbm = bars.getToolBarManager();
		tbm.add(refreshAction);
		bars.updateActionBars();
	}
	
	public void setFocus() {
		form.setFocus();
	}
	
	public void selectionChanged(IWorkbenchPart part, ISelection selection) {
		if(part instanceof RemoteView && selection instanceof ITreeSelection) {
			ITreeSelection current = (ITreeSelection) selection;
			if(selected != null && current.equals(selected)) {
				return;
			}
			selected = current;
			updateRemoteBuildInof(current);
		}
		else if(part instanceof CommonNavigator && selection instanceof ITreeSelection) {
			ITreeSelection current = (ITreeSelection) selection;	
			if(selected != null && current.equals(selected)) {
				return;
			}
			selected = current;
			Control[] children = form.getBody().getChildren();
			for(Control child : children) {
				child.dispose();
			}
		}
	}

}
